package cn.net.xyan.easy.spring.core.config;

import cn.net.xyan.easy.spring.core.config.value.ConfigKey;
import cn.net.xyan.easy.spring.core.config.value.IConfigValue;
import cn.net.xyan.easy.spring.core.exception.StatusAndMessageError;
import cn.net.xyan.easy.spring.core.utils.ExceptionUtils;
import cn.net.xyan.easy.spring.core.utils.JsonHelper;
import com.fasterxml.jackson.databind.JavaType;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Modifier;
import java.util.*;

/**
 * Created by xiashenpin on 16/1/16.
 */
public final class DefaultAutoConfigManager implements IAutoConfigManager{
    static DefaultAutoConfigManager __instance__;

    Logger logger = LoggerFactory.getLogger(DefaultAutoConfigManager.class);

    static public DefaultAutoConfigManager instance(){
        if (__instance__ == null)
            __instance__ = new DefaultAutoConfigManager();
        return  __instance__;
    }

    Map<ConfigKey,IConfigValue> configValues = new HashMap<>();

    private DefaultAutoConfigManager(){
        init(AutoConfigManagerFactory.doScan);
    }

    protected void init(boolean doScan){
        if (doScan){
            for (IAutoConfig autoConfig: createAutoConfig(AutoConfigManagerFactory.scanPackages)){
                autoConfig.onStartupConfigManager(this);
            }
        }
    }

    protected Set<IAutoConfig> createAutoConfig(String ... packages){
        return createAutoConfig(scanPackages(packages));
    }

    protected Set<IAutoConfig> createAutoConfig(Set< Class<? extends IAutoConfig> > cls){
        Set<IAutoConfig> autoConfigs = new LinkedHashSet<>();
        for (Class<? extends IAutoConfig> c : cls){
            try {
                autoConfigs.add(c.newInstance());
            } catch (Exception e) {
                ExceptionUtils.traceError(e,logger);
            }
        }

        return autoConfigs;
    }


    protected Set< Class<? extends IAutoConfig> > scanPackages(String ... packages){
        Reflections reflections = new Reflections(packages);

        Set<Class<? extends IAutoConfig>> classes = reflections.getSubTypesOf(IAutoConfig.class);
        Set<Class<? extends IAutoConfig>> result = new HashSet<>();
        for (Class<? extends IAutoConfig> cls : classes) {
            if (!cls.isInterface() && !Modifier.isAbstract(cls.getModifiers())) {
                result.add(cls);
            }
        }
        return result;
    }


    @Override
    public <T> T readValue(ConfigKey key, Class<T> tClass) {
        return readValue(key, JsonHelper.javaType(tClass));
    }

    @Override
    public <T> T readValue(ConfigKey key, JavaType type) {
        IConfigValue value = configValues.get(key);
        if (value!=null){
            return value.takeValue(type);
        }
        return null;
    }

    @Override
    public IConfigValue setupConfigValue(ConfigKey key) {
        IConfigValue value = configValues.get(key);
        if (value == null){
            try {
                value = AutoConfigManagerFactory.configValueType.newInstance();
                value.putConfigKey(key);
                value.putValue(key.getDefaultValue());
                configValues.put(key,value);
            } catch (Exception e) {
                throw new StatusAndMessageError(-99,e);
            }
        }
        return value;
    }
}
